package cn.backpackerxl.web.core;

import cn.backpackerxl.web.annotation.Ref;
import cn.backpackerxl.web.annotation.RequestMapping;
import cn.backpackerxl.web.model.ControllerMethod;
import cn.backpackerxl.web.model.Servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;

import static cn.backpackerxl.web.router.Router.router;
import static cn.backpackerxl.web.util.HandlerController.handlerRequest;
import static cn.backpackerxl.web.util.ScanController.getController;

/**
 * @Author:backpackerxl CreateTime:2022/4/17
 * FileName:BasicServlet
 **/
@WebServlet("/*")
public class BasicServlet extends HttpServlet {
    private final Set<Class<?>> controllers = new HashSet<>();
    private final Set<Servlet> servletSet = new HashSet<>();

    @Override
    public void init() {
        controllers.addAll(Objects.requireNonNull(getController()));
        servletSet.addAll(handlerRequest(controllers));
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        //设置跨域访问
        //允许跨域的主机地址
        resp.setHeader("Access-Control-Allow-Origin", "*");
        //允许跨域的请求方法GET, POST, HEAD 等
        resp.setHeader("Access-Control-Allow-Methods", "*");
        //重新预检验跨域的缓存时间 (s)
        resp.setHeader("Access-Control-Max-Age", "4200");
        //允许跨域的请求头
        resp.setHeader("Access-Control-Allow-Headers", "*");
        String baseUri = req.getRequestURI();
        List<String> router = router(baseUri);
        if (router.size() >= 3) {
            resp.getWriter().write("<h1>Router Error, please check your Controller</h1>");
            return;
        }
        if (router.size() > 0) {
            boolean flag = true;
            switch (router.size()) {
                case 1:
                    for (Servlet servlet : servletSet) {
                        if (!servlet.getClazz().isAnnotationPresent(RequestMapping.class)) {
                            for (ControllerMethod controllerMethod : servlet.getControllerMethod()) {
                                if (router.get(0).equals(controllerMethod.getPath())) {
                                    Method method = getMethod(servlet.getControllerMethod(), router.get(0));
                                    try {
                                        handlerReq(method, servlet, req, resp);
                                    } catch (Exception e) {
                                        e.printStackTrace();
                                    }
                                    flag = false;
                                    break;
                                }
                            }
                        }
                    }
                    if (flag) {
                        resp.setContentType("text/html;charset=utf-8");
                        resp.getWriter().write("<h1>404</h1><p>No resources were found under" + req.getRequestURI() + "</p> <p>" + new Date() + "</p>");
                    }
                    break;
                case 2:
                    Servlet servlet = null;
                    for (Servlet servlet1 : servletSet) {
                        if (router.get(0).equals(servlet1.getPath())) {
                            servlet = servlet1;
                            break;
                        }
                    }
                    if (servlet != null) {
                        for (ControllerMethod controllerMethod : servlet.getControllerMethod()) {
                            if (router.get(1).equals(controllerMethod.getPath())) {
                                Method method = getMethod(servlet.getControllerMethod(), router.get(1));
                                try {
                                    handlerReq(method, servlet, req, resp);
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                                flag = false;
                                break;
                            }
                        }
                        if (flag) {
                            resp.setContentType("text/html;charset=utf-8");
                            resp.getWriter().write("<h1>404</h1><p>No resources were found under" + req.getRequestURI() + "</p> <p>" + new Date() + "</p>");
                        }
                    } else {
                        resp.setContentType("text/html;charset=utf-8");
                        resp.getWriter().write("<h1>404</h1><p>No resources were found under" + req.getRequestURI() + "</p> <p>" + new Date() + "</p>");
                    }
                    break;
                default:
                    break;
            }
        } else {
            resp.setContentType("text/html;charset=utf-8");
            resp.getWriter().write("<h1>Congratulations</h1> <p>Your cn.backpackerxl.web app has started</p> <p>Don't worry, it's not an error page or a home page. It's just a startup page </p><p>" + new Date() + "</p>");
        }
    }

    public static void handlerReq(Method method, Servlet servlet, HttpServletRequest req, HttpServletResponse resp) throws Exception {
        Object res = null;
        try {
            Parameter[] parameters = method.getParameters();
            Object[] obj = new Object[parameters.length];
            int idx = 0;
            for (Parameter parameter : parameters) {
                if (parameter.isAnnotationPresent(Ref.class)) {
                    obj[idx] = req.getParameter(parameter.getName());
                }
                if (parameter.getType().equals(HttpServletRequest.class)) {
                    obj[idx] = req;
                }
                if (parameter.getType().equals(HttpServletResponse.class)) {
                    obj[idx] = resp;
                }
                idx++;
            }
            res = method.invoke(servlet.getClazz().newInstance(), obj);
        } catch (Exception e) {
            e.printStackTrace();
        }
        assert res != null;
        resp.setContentType("text/json;charset=utf-8");
        resp.getWriter().write(res.toString());
    }

    public static Method getMethod(List<ControllerMethod> controllerMethods, String path) {
        Method m = null;
        for (ControllerMethod controllerMethod : controllerMethods) {
            if (path.equals(controllerMethod.getPath())) {
                m = controllerMethod.getMethod();
            }
        }
        return m;
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
